// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rbchat;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import com.bumptech.glide.Glide;
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
import com.bumptech.glide.load.model.GlideUrl;
import com.eva.android.HttpService4A;
import com.eva.android.HttpsUtils;
import com.eva.android.OkHttpsUtils;
import com.tencent.bugly.crashreport.CrashReport;
import com.x52im.rbchat.logic.main.LoginActivity;
import com.x52im.rbchat.network.http.bigfile.BigFileUploadManager;

import java.io.InputStream;
import java.lang.Thread.UncaughtExceptionHandler;

import androidx.multidex.MultiDexApplication;
import okhttp3.Call;

/**
 * 本类是整个APP的全局Aplication对象实现类.
 * <p>
 * 根据Android程序设计最佳实践技巧，系统中的所有全局变量应存放于本类或依赖于本类生命周期的类中.
 * 
 * @author Jack Jiang(http://www.52im.net/space-uid-1.html)
 * @version 1.0
 */
public class MyApplication extends MultiDexApplication
{
	//----------------------------- APP 异常自救代码 START （TODO - 集成到你的APP中时，建议保留此机制）
	/**
	 *  APP健康状态检查标识。
	 *  <pre>
	 * 【问题原因】：当Android APP被一些安全助手、内存清理应用、或在某些手机的系统权限设置中关闭了本应用的某个权限后，
	 *             将导致Android APP处于一种脏现场状态——即进程被杀、内存数据被清、但恶心的是Activity的栈还在。这
	 *             将导致，再次进入到APP时会仍然回到之前APP里打开的页面（并且有完整的activity栈逻辑），进而因数据
	 *             被清、进程已不在等，导致一系列的功能逻辑错乱、崩溃等。
	 * 【当前方案】：这是Android APP复杂诡异的经典问题之一，目前没有更好的方案来处理。RainbowChat目前使用了微信一样
	 *             的处理逻辑，就是当进入到APP界面时，检查本标识，如果为-1表示从非正常的现场恢复而来，进来直接跳转到
	 *             启动页面，从而保证APP现场的正常恢复。
	 * 【标识含义】：当本标识为默认值-1时，表示从非正常状态过回到APP的，当为1时表示正常状态。
	 * 【正常逻辑】：正常的RainbowChat APP启动逻辑是——
	 *             1）以AppStart.java闪屏类为主程序入口，启动APP；
	 *             2）闪屏完成后自动跳转到LoginActivity.java登陆界面；
	 *			   3）登陆完成后进入主界面（即PortalActivity.java类）。
	 * 【恢复逻辑】：当任一BaseActivity子类（目前包括登陆界面在内的所有rainbowchat内页都是其子类，但或PortalActivity
	 *             不是，所以PortalActivity中会有单独的处理逻辑）或PortalActivity类中，onCreate方法检测到本标识为-1
	 *             ，即表示需要进行APP恢复。
	 * 【具体实施】：具体分两步——
	 *             1）当在BaseActivity子类onCreate方法中检测到本标识非正常时，无条件直接跳转到PortalActivity主类；
	 *             2）当在PortalActivity主类onCreate方法中检测到非正常时，继续接力跳转到AppStart.java闪屏类（从而
	 *                达到重启APP的效果）。
	 *             注意：跳到PortalActivity类后，能保证优雅的清理掉PortalActivity之上的整个activity栈吗？能，因为
	 *                  它拥有 android:launchMode="singleTop" 属性，且有"Intent.FLAG_ACTIVITY_CLEAR_TOP" flag，
	 *                  从而保证只要跳转到PortalActivity类，就能由Android的机制优雅地清理掉其它activity栈残留。关于
	 *                  这两个属生的文章，请自行百度。
	 * 【编码实现】：具体步骤为——
	 *             1）当AppStart.java闪屏启动成功时，设置本标识为1（当出现本方案讨论的异常情况时，显然此标识肯定为其默认值-1了）；
	 *             2）当BaseActivity子类检测到本标识为-1时，进入异常恢复逻辑，见"【具体实施】"；
	 *             3）PortalActivity类检测到本标识为-1时，继续接力处理异常恢复逻辑。
	 * 【方案灵感】：https://blog.csdn.net/lvzishen123/article/details/51519451
	 *  </pre>
	 */
	public static int appHearthCheckFlag = -1;
	//----------------------------- APP 异常自救代码 END

	//----------------------------- APP的默认异常处理 START （TODO - 集成到你的APP中时，建议保留以下代码）
	/** APP的默认异常处理Handler引用 */
	private UncaughtExceptionHandler defaultSystemUncaughtExceptionHandler = null;
	/** RainbowChat的自定义APP异常处理Handler（用于APP意外崩溃时捕获异常并处理之） */
	private Thread.UncaughtExceptionHandler rainbowChatUncaughtExceptionHandler = (thread, ex) -> {
		if (ex != null)
			Log.e("_unCaughtExHandler", ex.getMessage(), ex);

		// 优雅地退出APP
		LoginActivity.systemExit(MyApplication.this, true);
		// 并将此异常也传递给系统默认的异常处理机制
		// re-throw critical exception further to the os (important)
		defaultSystemUncaughtExceptionHandler.uncaughtException(thread, ex);
	};
	//----------------------------- APP的默认异常处理 END

	/** RainbowChat中的IM相关数据模型、全局变量等管理类 */
	private IMClientManager imClientManager = null;

	/** 通过ActivityLifecycleCallbacks来实现检测APP是处理前台还是后台运行状态 */
	private ActivityLifecycleCallbacksImpl activityLifecycle = new ActivityLifecycleCallbacksImpl();
	
	/** 本Application的实例引用（方便代码中使用全局Application） */
	private static MyApplication self = null;
	
	/**
	 * Application的onCreate实现方法。
	 */
	public void onCreate() {
        super.onCreate();

        // 保存自已，便于代码中使用本Application
		self = this;

		//----------------------------- APP的默认异常处理 START （TODO - 集成到你的APP中时，建议保留此代码）
        // 保存一下Android系统默认的异常处理handeler
		this.defaultSystemUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置RainbowChat自定义的异常处理handeler
        Thread.setDefaultUncaughtExceptionHandler(rainbowChatUncaughtExceptionHandler);

		//----------------------------- Bug异常信息收集 START （TODO - 集成到你的APP中时，你可以去掉以下代码）
		// TODO: 腾讯bugly初始化，请将 "14fca0b502" 替换成你自已的腾讯bugly APPID，
		//  详见：https://bugly.qq.com/docs/user-guide/instruction-manual-android/?v=20180709165613
		CrashReport.initCrashReport(getApplicationContext(), "?????????", false);

		//----------------------------- RainbowChat的全局核心管理器 START （TODO - 集成到你的APP中时，务必保留此代码！）
		// 实例化IM相关数据模型、全局变量等管理类
		this.imClientManager = new IMClientManager(this);

		//----------------------------- APP的前台后台运行运行状态检测和记录 START （TODO - 集成到你的APP中时，务必保留此代码！）
		// 实例化ActivityLifecycleCallbacks对象，实现APP的前台后台运行运行状态检测和记录
		this.registerActivityLifecycleCallbacks(activityLifecycle);

		//----------------------------- 解决glide加载https证书问题 START （TODO - 集成到你的APP中时，建议保留此代码）
		// 解决glide加载https证书问题（否则自签名证书等情况下，无法正常加载https图片），
		// 参考资料：https://zhuanlan.zhihu.com/p/101996036?from_voters_page=true
		if(HttpsUtils.isHttps(Const.HTTP_SERVER_ROOT_URL))
		try {
			Log.i(BigFileUploadManager.class.getSimpleName(), "【HTTPS】将为Glide启用https设置，HTTP_SERVER_ROOT_URL="+Const.HTTP_SERVER_ROOT_URL);
			Glide.get(this).getRegistry().replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory((Call.Factory) OkHttpsUtils.getOkHttpsClient4Glide()));
		} catch (Exception e) {
			Log.e(MyApplication.class.getSimpleName(), e.getMessage(), e);
		}
	}

	/**
	 * 获得RainbowChat中的IM相关数据模型、全局变量等管理类的实例引用。
	 *
	 * @return 全局实例引用
	 */
	public IMClientManager getIMClientManager() {
		return imClientManager;
	}

	/**
	 * APP是否处于前台运行。
	 *
	 * @return true表示是，否则不是
	 */
	public boolean isAppInForeground() {
		if(this.activityLifecycle != null) {
			return this.activityLifecycle.getActivityCount() > 0;
		}
		return false;
	}
	
	/**
	 * 一个获得本application对象的方便方法.<br>
	 * 相当于在你的activity中调用：(MyApplication)this.getApplicationContext()，本方法只是为了简化操作而已.
	 * 
	 * @param context 上下文对象引用
	 * @return MyApplication对象引用
	 * @deprecated 建议请使用方法getInstance2()替代本方法，调用更方便
	 */
	public static MyApplication getInstance(Context context) {
		return (MyApplication)context.getApplicationContext();
	}

	/**
	 * 一个获得本application对象的方便方法.
	 *
	 * @return MyApplication对象引用
	 */
	public static MyApplication getInstance2() {
		return self;
	}

	/**
	 * 通过ActivityLifecycleCallbacks来实现检测APP是处理前台还是后台运行状态。
	 * 参考资料：https://blog.csdn.net/u011386173/article/details/79095757
	 * @since 8.3
	 */
	private class ActivityLifecycleCallbacksImpl implements ActivityLifecycleCallbacks {
		private final static String TAG = "ActivityLifeCallback";

		private int mActivityCount = 0;

		@Override
		public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
			Log.d(TAG,"【检测APP前后台状态】onActivityCreated （mActivityCount="+mActivityCount+"）");
		}

		@Override
		public void onActivityStarted(Activity activity) {
			mActivityCount++;
			Log.d(TAG,"【检测APP前后台状态】onActivityStarted【注意】（mActivityCount="+mActivityCount+"）");
		}

		@Override
		public void onActivityResumed(Activity activity) {
			Log.d(TAG,"【检测APP前后台状态】onActivityResumed （mActivityCount="+mActivityCount+"）");
		}

		@Override
		public void onActivityPaused(Activity activity) {
			Log.d(TAG,"【检测APP前后台状态】onActivityPaused （mActivityCount="+mActivityCount+"）");
		}

		@Override
		public void onActivityStopped(Activity activity) {
			mActivityCount--;
			Log.d(TAG,"【检测APP前后台状态】onActivityStopped【注意】（mActivityCount="+mActivityCount+"）");
		}

		@Override
		public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
			Log.d(TAG,"【检测APP前后台状态】onActivitySaveInstanceState （mActivityCount="+mActivityCount+"）");
		}

		@Override
		public void onActivityDestroyed(Activity activity) {
//			Log.d(TAG,"onActivityDestroyed");
			Log.d(TAG,"【检测APP前后台状态】onActivityDestroyed （mActivityCount="+mActivityCount+"）");
		}

		public int getActivityCount() {
			return mActivityCount;
		}
	}
}
